<template>
	<text class="xl-count-to" :style="{ fontSize: $xl.parseUnit(fontSize, 'rpx'), fontWeight: bold ? 'bold' : 'normal', color: color }" >{{ displayValue }}</text>
</template>

<script setup>
	import propsData from './props.js'
	const props = defineProps(propsData)
	import { ref, watch, computed } from 'vue'
	const emits = defineEmits(['end'])
	
	const displayValue = ref(formatNumber(props.start))
	
	const localStartVal = ref(props.start)
	const localDuration = ref(props.duration)
	let startTime = null // 开始的时间
	let paused = false // 是否暂停
	let rAF = null
	let lastTime = 0 // 上一次的时间
	let remaining = null // 停留的时间
	let printVal = null
	let timestamp = null // 时间戳
	
	let countDown = computed(()=>{
		return props.start > props.num;
	})
	
	watch(() => props.start, ( newValue, oldValue ) => {
		props.autoplay && start();
	})
	watch(() => props.num, ( newValue, oldValue ) => {
		props.autoplay && start();
	})
	
	props.autoplay && start();
	
	function easingFn(t, b, c, d) {
		return (c * (-Math.pow(2, (-10 * t) / d) + 1) * 1024) / 1023 + b;
	}
	// 开始滚动数字
	function start() {
		localStartVal.value = props.start;
		startTime = null;
		localDuration.value = props.duration;
		paused = false;
		rAF = requestAnimationFrame(count);
	}
	function requestAnimationFrame(callback) {
		const currTime = new Date().getTime();
		// 为了使setTimteout的尽可能的接近每秒60帧的效果
		const timeToCall = Math.max(0, 16 - (currTime - lastTime));
		const id = setTimeout(() => {
			callback(currTime + timeToCall);
		}, timeToCall);
		lastTime = currTime + timeToCall;
		return id;
	}
	function cancelAnimationFrame(id) {
		clearTimeout(id);
	}
	
	// 暂定状态，重新再开始滚动；或者滚动状态下，暂停
	function reStart() {
		if (paused) {
			resume();
			paused = false;
		} else {
			stop();
			paused = true;
		}
	}
	// 暂停
	function stop() {
		cancelAnimationFrame(rAF);
	}
	// 重新开始(暂停的情况下)
	function resume() {
		if (!remaining) return
		startTime = 0;
		localDuration.value = remaining;
		localStartVal.value = printVal;
		requestAnimationFrame(count);
	}
	// 重置
	function reset() {
		startTime = null;
		cancelAnimationFrame(rAF);
		displayValue.value = formatNumber(props.start);
	}
	function count(timestp) {
		if (!startTime) startTime = timestp;
		timestamp = timestp;
		const progress = timestp - startTime;
		remaining = localDuration.value - progress;
		if (props.easing) {
			if (countDown.value) {
				printVal = localStartVal.value - easingFn(progress, 0, localStartVal.value - props.num, localDuration.value);
			} else {
				printVal = easingFn(progress, localStartVal.value, props.num - localStartVal.value, localDuration.value);
			}
		} else {
			if (countDown.value) {
				printVal = localStartVal.value - (localStartVal.value - props.num) * (progress / localDuration.value);
			} else {
				printVal = localStartVal.value + (props.num - localStartVal.value) * (progress / localDuration.value);
			}
		}
		if (countDown.value) {
			printVal = printVal < props.num ? props.num : printVal;
		} else {
			printVal = printVal > props.num ? props.num : printVal;
		}
		displayValue.value = formatNumber(printVal) || 0;
		if (progress < localDuration.value) {
			rAF = requestAnimationFrame(count);
		} else {
			emits('end')
		}
	}
	// 判断是否数字
	function isNumber(val) {
		return !isNaN(parseFloat(val));
	}
	function formatNumber(num) {
		// 将num转为Number类型，因为其值可能为字符串数值，调用toFixed会报错
		num = Number(num);
		num = num.toFixed(Number(props.decimals));
		num += '';
		const x = num.split('.');
		let x1 = x[0];
		const x2 = x.length > 1 ? props.decimal + x[1] : '';
		const rgx = /(\d+)(\d{3})/;
		if (props.separator && !isNumber(props.separator)) {
			while (rgx.test(x1)) {
				x1 = x1.replace(rgx, '$1' + props.separator + '$2');
			}
		}
		return x1 + x2;
	}
	function destroyed() {
		cancelAnimationFrame(rAF);
	}
</script>

<style lang="scss" scoped>
.xl-count-to {
	/* #ifndef APP-NVUE */
	display: inline-flex;
	/* #endif */
	text-align: center;
}
</style>
